#include "words_queue.h"
#include <cmath>

words_queue::words_queue (int _size, bool _long_mask){
  if (_size < words_queue::MIN_WORDS_QUEUE_SIZE){
    _size = words_queue::MIN_WORDS_QUEUE_SIZE;
  }
  size = _size;
  long_mask = _long_mask;
  letters = new char [size];
  qbeg = 0;
  qend = 0;
}

words_queue::~words_queue(){
  delete[] letters;
}

const bool words_queue::is_empty() const{
  return (qbeg == qend);
}

const i64 words_queue::get_number_of_letters() const{
  i64 res = qend - qbeg;
  if (res < 0){
    if (long_mask) res += 2 * size;
    else res += 4 * size;
  }
  return res;
}

void words_queue::inc_index(i64 &i){
  i++;
  if (long_mask){
    if ((i>>1) >= size){
      i = 0;
    }
  }
  else{
    if ((i>>2) >= size){
      i = 0;
    }
  }
}

const bool words_queue::is_index(i64 &i) const{
  return (qbeg == i);
}

const bool words_queue::push_symbol(char ch){
  ch -= 'a';
  if (long_mask){
    int mod = (int)(qend&1);
    letters[qend>>1] &= ~(LONG_MASK << (4*mod));
    letters[qend>>1] |= (ch << (4*mod));
  }
  else{
    int mod = (int)(qend&3);
    letters[qend>>2] &= ~(SHORT_MASK << (2*mod));
    letters[qend>>2] |= (ch << (2*mod));
  }
  inc_index(qend);
  return (qbeg != qend);
}

const bool words_queue::push_marker(i64 &mpos){
  mpos = qend;
  return push_symbol('a'); // We can push any symbol actually
}

const bool words_queue::push_word(const char *word){
  int i;
  for (i=0; word[i]; i++){
    if (!push_symbol(word[i])) return false;
  }
  return true;
}

const char words_queue::peek_symbol() const{
  if (long_mask){
    int mod = (int)(qbeg&1);
    return (letters[qbeg>>1] & (LONG_MASK << (4*mod))) >> (4*mod);
  }
  else{
    int mod = (int)(qbeg&3);
    return (letters[qbeg>>2] & (SHORT_MASK << (2*mod))) >> (2*mod);
  }
}

const char words_queue::pop_symbol(){
  char res = peek_symbol();
  inc_index(qbeg);
  return res;
}

//void create_words_queue(input_data & INPUT, int size, words_queue * WORDS_QUEUE){
words_queue * create_words_queue(input_data & INPUT, int size){
  int qsize;
  if (size != -1){
    qsize = size;
  }
  else{ // define size automatically
    double max_size = INPUT.ROOT_LENGTH * pow((double)INPUT.ALPH_SIZE, (double)INPUT.ROOT_LENGTH);
    if (sizeof(char) * max_size < LITTLE_MEMORY){
      qsize = (int)max_size + 1;
    }
    else{
      qsize = QUEUE_MEMORY_STEP;
      while (1){
        try{
          char * buff = new char [qsize + QUEUE_MEMORY_STEP];
          delete[] buff;
          qsize += QUEUE_MEMORY_STEP;
        }
        catch(...){
          break;
        }
      }
      qsize >>= 1;
    }
  }
  bool use_long_mask = (INPUT.ALPH_SIZE > 4);
  //WORDS_QUEUE = new words_queue(qsize, use_long_mask);  
  return new words_queue(qsize, use_long_mask);
}
